
#include <BFont>
#include <BPainter>
#include <BMouseEvent>
#include <BKeyEvent>
#include <BScrollBar>

#include "IAlignment.h"
#include "IComboBoard.h"
#include "member_BObject.h"
#include "member_BComboItem.h"

using namespace BWE;

IComboBoard::IComboBoard(BArray<BComboItemHolder>* items, BComboBox* comboBox) : BScrollArea(comboBox)
{
	this->items = items;
	this->setVisible(false);
	this->setScrollPolicy(ScrollPolicy_Floating);

	host = comboBox;
	dirty = true;
	itemSpace = 3;
	hoveredItem = 0;
	pressedItem = 0;

	arrow = new BButton(Graph_Arrow_Down_Fill);
	arrow->setName("arrow");
	arrow->setCheckable(true);
	arrow->setEnabled(false);

	connect(arrow, Signal_Checked, &IComboBoard::slotArrowChecked);

}
IComboBoard::~IComboBoard()
{

}

void IComboBoard::freshItemRects()
{
	const BFont* font = this->font();
	if (!font)
		return;

	Form form = host->form();
	if (form == Form_None)
	{
		for (int i = 0; i < items->size(); i++)
		{
			BComboItem* item = (*items)[i];
			if (item->icon() || item->image() || item->graph())
			{
				form = Form_IconText;
				break;
			}
		}
	}
	BSize realIconSize;
	if (form != Form_TextOnly && form != Form_None)
	{
		realIconSize = iconSize;
		if (font)
		{
			if (realIconSize.width() < 1)
				realIconSize.width() = font->size();
			if (realIconSize.height() < 1)
				realIconSize.height() = font->size();
		}
	}

	int cw = this->centerRect().width();
	int ch = this->centerRect().height();

	contentSize.reset();
	int rowHeight = bMax(realIconSize.height(), font->size());
	IAlignment alignment;
	int y = 0;
	for (int i = 0; i < items->size(); i++)
	{
		BComboItem* item = (*items)[i];

		item_member(item)->rect.set(0, y, cw, rowHeight);

		alignment.ia_align = host->align();
		alignment.ia_form = form;
		alignment.ia_spacing = host->spacing();
		alignment.ia_textSize = item_member(item)->textSize;
		alignment.ia_imageSize = realIconSize;
		alignment.adjust(item_member(item)->rect);

		item_member(item)->iconRect = alignment.ia_imageRect;
		item_member(item)->textRect = alignment.ia_textRect;

		y += rowHeight + itemSpace;
		contentSize.width() = bMax(contentSize.width(), alignment.ia_hintSize.width());
		contentSize.height() += rowHeight + itemSpace;
	}
	if (items->size())
		contentSize.height() -= itemSpace;

	BRect area = this->area();
	area.setSize(contentSize);
	this->setArea(area);
	this->verticalScrollBar()->setStep(rowHeight);
}

void IComboBoard::slotArrowChecked(BObject* object, const BValue& value)
{
	BButton* arrow = (BButton*)object;
	if (arrow->checked())
		this->popup();
	else
		this->hide();
	if (this->visible())
	{
		BRect hostRect = host->rect();
		BRect rootRect = host->mapFrom(host->root(), host->root()->size());
		BSize size = this->sizeHint();
		if (size.width() < hostRect.width())
			size.width() = hostRect.width();
		if (size.width() > rootRect.width())
			size.width() = rootRect.width();
		int spaceUp = -rootRect.top();
		int spaceDown = rootRect.bottom() - hostRect.height();
		if (spaceDown < size.height() && spaceDown < spaceUp)
		{
			size.height() = bMin(size.height(), spaceUp);
			this->setSize(size);
			this->setPos(0, -this->height());
		}
		else
		{
			size.height() = bMin(size.height(), spaceDown);
			this->setSize(size);
			this->setPos(0, hostRect.height());
		}
		object_member(host)->emit(Signal_Popup, this);
	}
}

BSize IComboBoard::sizeHint() const
{
	if (dirty)
	{
		((IComboBoard*)this)->freshItemRects();
		dirty = false;
	}
	return contentSize + BWidget::sizeHint();
}

void IComboBoard::scrollArea()
{
	showBegin = -1;
	showEnd = -1;
	BRect crect = this->centerRect();
	BPoint offset = this->offset();
	for (int i = 0; i < items->size(); i++)
	{
		BComboItem* item = (*items)[i];
		BRect rect = item_member(item)->rect + offset;
		if (rect.intersect(crect))
		{
			showBegin = i;
			showEnd = i;
			break;
		}
	}
	if (showBegin >= 0)
	{
		while (++showEnd < items->size())
		{
			BComboItem* item = (*items)[showEnd];
			BRect rect = item_member(item)->rect + offset;
			if (rect.top() > crect.bottom())
				break;
		}
	}
	this->fresh();
	BScrollArea::scrollArea();
}

void IComboBoard::resizeEvent(const BEvent& event)
{
	dirty = true;
	BScrollArea::resizeEvent(event);
}

void IComboBoard::styleEvent(const BEvent& event)
{
	const BStyle* style = this->realStyle();
	dirty = true;
	BScrollArea::styleEvent(event);
}
void IComboBoard::freshEvent(const BEvent& event)
{
	if (dirty)
	{
		freshItemRects();
		dirty = false;
	}
	BScrollArea::freshEvent(event);
}
void IComboBoard::paintEvent(const BEvent& event)
{
	BScrollArea::paintEvent(event);

	const BRect& crect = this->centerRect();
	BPoint offset = this->offset();
	BComboItem* current = host->currentItem();

	BPainter painter(this);

	painter.setClip(crect);
	for (int i = showBegin; i < showEnd; i++)
	{
		BComboItem* item = (*items)[i];
		BRect rect = item_member(item)->rect + offset;
		BRect iconRect = item_member(item)->iconRect + offset;
		BRect textRect = item_member(item)->textRect + offset;

		StyleRole itemColorRole = Color_Item;
		StyleRole textColorRole = Color_Text;
		State state = State_Normal;
		if (!item->enabled())
		{
			state = State_Disable;
			textColorRole = Color_Text_Disable;
			itemColorRole = Color_Item_Disable;
		}
		else if (pressedItem == item)
		{
			state = State_Pressed;
			textColorRole = Color_Text_Checked;
			itemColorRole = Color_Item_Checked;
		}
		else if (hoveredItem == item)
		{
			state = State_Hovered;
			textColorRole = Color_Text_Hovered;
			itemColorRole = Color_Item_Hovered;
		}
		if (painter.setColor(itemColorRole))
		{
			painter.fillRect(rect);
		}
		if (painter.setColor(textColorRole))
		{
			if (item_member(item)->t_text.size())
				painter.drawText(textRect, item_member(item)->t_text);
			else
				painter.drawText(textRect, item_member(item)->text);
		}
		BImage* image = item_member(item)->image;
		if (BIcon* icon = item_member(item)->icon)
		{
			image = icon->image(state);
		}
		if (image)
		{
			painter.setColor(item_member(item)->color);
			painter.drawImage(iconRect, image, true);
		}
		else if (item_member(item)->graph)
		{
			painter.setColor(item_member(item)->color);
			painter.setLineSmooth(true);
			painter.drawGraph(iconRect % 80, item_member(item)->graph);
		}
	}
}

void IComboBoard::keyDownEvent(const BKeyEvent& keyEvent)
{
	if (keyEvent.key() == Key_Enter)
	{
		host->setCurrentItem(hoveredItem);
		this->hide();
		return;
	}

	int index = items->find(hoveredItem);
	if (keyEvent.key() == Key_Up)
	{
		index--;
		if (index < 0)
			index = 0;
	}
	if (keyEvent.key() == Key_Down)
	{
		index++;
		if (index >= items->size())
			index = items->size() - 1;
	}
	if (hoveredItem != items->at(index))
	{
		hoveredItem = items->at(index);
		fresh();
	}
}

void IComboBoard::mouseEnterEvent(const BMouseEvent& mouseEvent)
{
	if (hoveredItem)
	{
		hoveredItem = 0;
		this->fresh();
	}
	BScrollArea::mouseEnterEvent(mouseEvent);
}
void IComboBoard::mouseLeaveEvent(const BMouseEvent& mouseEvent)
{
	if (hoveredItem)
	{
		hoveredItem = 0;
		this->fresh();
	}
	BScrollArea::mouseLeaveEvent(mouseEvent);
}

void IComboBoard::mouseClickEvent(const BMouseEvent& mouseEvent)
{
	if (hoveredItem && hoveredItem->enabled())
	{
		host->setCurrentItem(hoveredItem);
		object_member(host)->emit(Signal_ItemClicked, hoveredItem);
		arrow->setChecked(false);
	}
}
void IComboBoard::mouseMoveEvent(const BMouseEvent& mouseEvent)
{
	BScrollArea::mouseMoveEvent(mouseEvent);

	const BPoint& pos = mouseEvent.pos();
	BPoint offset = this->offset();
	BComboItem* hovered_item = 0;
	for (int i = showBegin; i < showEnd; i++)
	{
		BComboItem* item = (*items)[i];
		BRect rect = item_member(item)->rect + offset;
		if (rect.contain(pos))
		{
			if(item->enabled())
				hovered_item = item;
			break;
		}
	}
	if (hoveredItem != hovered_item)
	{
		hoveredItem = hovered_item;
		object_member(host)->emit(Signal_ItemHovered, hoveredItem);
		fresh();
	}
}
